﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace 社畜
{
	public partial class 社畜電卓 : Form
	{
		#region コンストラクタ
		//=========================================================================================
		/// <summary>コンストラクタ</summary>
		//=========================================================================================
		public 社畜電卓()
		{
			InitializeComponent();
		}		
		#endregion

		#region イベントハンドラ
		//=========================================================================================
		/// <summary>ウィンドウロード</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void OverTimeClaculatorWindow_Load( object sender, EventArgs e )
		{		
			// 初期化
			this.InitializeCalendar( DateTime.Now );
		}

		//=========================================================================================
		/// <summary>Form閉じるイベント</summary>
		//=========================================================================================
		private void 社畜電卓_FormClosed( object sender, FormClosedEventArgs e )
		{
			Application.Exit();
		}

		//=========================================================================================
		/// <summary>年変化イベント</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void YearUpDown_ValueChanged( object sender, EventArgs e )
		{
			if( calenderMode != CalenderMode.LoadComplate )
			{
				// 画面ロードが完了していない場合は行わない
				return;
			}

			// 新しい日付設定
			DateTime changeDate = new DateTime( ( int )this.YearUpDown.Value, ( int )this.MonthUpDown.Value, 1 );

			// リスト初期化
			this.InitializeCalendar( changeDate );
		}

		//=========================================================================================
		/// <summary>月変化イベント</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void MonthUpDown_ValueChanged( object sender, EventArgs e )
		{
			if( calenderMode != CalenderMode.LoadComplate )
			{
				// 画面ロードが完了していない場合は行わない
				return;
			}

			// 新しい日付設定
			DateTime changeDate = new DateTime( ( int )this.YearUpDown.Value, ( int )this.MonthUpDown.Value, 1 );

			// リスト初期化
			this.InitializeCalendar( changeDate );
		}

		//=========================================================================================
		/// <summary></summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void CalendarGrid_CurrentCellDirtyStateChanged( object sender, EventArgs e )
		{
			if( this.CalendarGrid.CurrentCellAddress.X == GridColumns.OffDay || 
				this.CalendarGrid.CurrentCellAddress.X == GridColumns.OnTime ||
				this.CalendarGrid.CurrentCellAddress.X == GridColumns.OverTime && 
				this.CalendarGrid.IsCurrentCellDirty )
			{
				//コミットする
				this.CalendarGrid.CommitEdit( DataGridViewDataErrorContexts.Commit );
			}
		}

		//=========================================================================================
		/// <summary>セル状態変化イベント</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void CalendarGrid_CellValueChanged( object sender, DataGridViewCellEventArgs e )
		{
			if( 0 <= e.RowIndex )
			{
				switch( e.ColumnIndex )
				{
				case GridColumns.OffDay:
					if( ( bool )this.CalendarGrid[ GridColumns.OffDay, e.RowIndex ].Value == true )
					{
						this.CalendarGrid[ GridColumns.OnTime, e.RowIndex ].Value = false;
					}
					break;

				case GridColumns.OnTime:
					if( ( bool )this.CalendarGrid[ GridColumns.OnTime, e.RowIndex ].Value == true )
					{
						this.CalendarGrid[ GridColumns.OffDay, e.RowIndex ].Value = false;
					}
					break;

				case GridColumns.OverTime:
					if( this.isAutoSet == false )
					{
						this.AdjustCalc();
					}
					break;
				}
			}
		}

		//=========================================================================================
		/// <summary>計算ボタンクリックイベント</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void CalcButton_Click( object sender, EventArgs e )
		{
			this.NewCalc();
		}

		//=========================================================================================
		/// <summary>タスクトレイに格納ボタンクリックイベント</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void MinimizeButton_Click( object sender, EventArgs e )
		{
			// 閉じるボタン押下でアプリ終了ではなくフォームを非表示にする
			this.Visible = false;

			// タスクトレイアイコンを表示
			常駐アイコン.Visible = true;

			// タスクバー非表示
			this.Owner.ShowInTaskbar = false;
		}

		//=========================================================================================
		/// <summary>閉じるボタンクリックイベント</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void CloseButton_Click( object sender, EventArgs e )
		{
			this.Close();
		}

		//=========================================================================================
		/// <summary>常駐アイコンダブルクリックイベント</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void 常駐アイコン_DoubleClick( object sender, EventArgs e )
		{
			// アイコンをトレイから取り除く
			常駐アイコン.Visible = false;

			// フォームの表示
			this.Visible = true;
			if( this.WindowState == FormWindowState.Minimized )
			{
				// 最小化をやめる
				this.WindowState = FormWindowState.Normal;
			}

			// タスクバー表示
			this.Owner.ShowInTaskbar = true;

			// フォームをアクティブにする
			this.Activate();
		}

		//=========================================================================================
		/// <summary>コンテキストメニューの開く押下</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void 開くメニューClick( object sender, EventArgs e )
		{
			// アイコンをトレイから取り除く
			常駐アイコン.Visible = false;

			// フォームの表示
			this.Visible = true;
			if( this.WindowState == FormWindowState.Minimized )
			{
				// 最小化をやめる
				this.WindowState = FormWindowState.Normal;
			}

			// タスクバー表示
			this.Owner.ShowInTaskbar = true;

			// フォームをアクティブにする
			this.Activate();
		}

		//=========================================================================================
		/// <summary>コンテキストメニューの終了押下</summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void 終了メニューClick( object sender, EventArgs e )
		{
			常駐アイコン.Visible = false; // アイコンをトレイから取り除く
			Application.Exit(); // アプリケーションの終了
		}

		//=========================================================================================
		/// <summary>
		/// 残業上限チェックボックス変更イベント
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void MaxOverTimeCheckBox_CheckedChanged( object sender, EventArgs e )
		{
			if( this.MaxOverTimeCheckBox.Checked )
			{
				this.syachikuAPI.CalcType = 計算タイプ.上限指定タイプ;
				this.MaxOverTimeUpDown.Enabled = true;
			}
			else
			{
				this.syachikuAPI.CalcType = 計算タイプ.分散タイプ;
				this.MaxOverTimeUpDown.Enabled = false;
			}
		}

		//=========================================================================================
		/// <summary>
		/// 残業上限時間変更イベント
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		//=========================================================================================
		private void MaxOverTimeUpDown_ValueChanged( object sender, EventArgs e )
		{
			this.syachikuAPI.LimitTime = ( double )this.MaxOverTimeUpDown.Value;
		}
		#endregion

		#region 内部メソッド
		//=========================================================================================
		/// <summary>設定項目情報初期化</summary>
		//=========================================================================================
		private void InitializeSettingInfo()
		{
			// ノルマ時間
			this.QuataUpDown.Value = ( decimal )this.syachikuAPI.QuataTime;

			// 超過時間
			this.OverTimeUpDown.Value = ( decimal )this.syachikuAPI.CurrentOverTime;

			//
			this.MaxOverTimeCheckBox.Checked = ( this.syachikuAPI.CalcType == 計算タイプ.上限指定タイプ );

			//
			this.MaxOverTimeUpDown.Enabled = ( this.syachikuAPI.CalcType == 計算タイプ.上限指定タイプ );
			this.MaxOverTimeUpDown.Value = ( decimal )this.syachikuAPI.LimitTime;
		}

		//=========================================================================================
		/// <summary>カレンダー初期化</summary>
		//=========================================================================================
		private void InitializeCalendar( DateTime newDate )
		{
			this.newDate = newDate;

			// メイン画面非表示
			this.Opacity = 0;
			this.Refresh();

			// 待ち受け画像表示
			this.SplashScreen.Opacity = 0.0;
			this.SplashScreen.Show( this );
			this.SplashScreen.Refresh();

			this.calenderMode = CalenderMode.FadeIn;

			// タイマー起動
			this.splashScreenTimer = new Timer();
			this.splashScreenTimer.Interval = 1;
			this.splashScreenTimer.Tick += new EventHandler( InitializeCalendarProc );
			this.splashScreenTimer.Start();
		}

		//=========================================================================================
		/// <summary>カレンダー初期化後始末</summary>
		//=========================================================================================
		private void AfterInitialize()
		{
			// メイン画面表示
			this.Opacity = 1;
			this.Refresh();

			// 待ち受け画像非表示
			this.SplashScreen.Hide();
			this.SplashScreen.Refresh();

			// グリッドにフォーカス移動
			this.CalendarGrid.Focus();

			// 超過時間をクリア
			this.OverTimeUpDown.Value = 0;

			// タイマー停止
			this.splashScreenTimer.Stop();
			this.splashScreenTimer.Dispose();
			this.splashScreenTimer = null;

			// 設定項目初期化
			this.InitializeSettingInfo();

			// 現在の年月を設定
			this.YearUpDown.Value = this.syachikuAPI.ScheduleMonth.Year;
			this.MonthUpDown.Value = this.syachikuAPI.ScheduleMonth.Month;
		}

		//=========================================================================================
		/// <summary>カレンダー初期化ハンドラ</summary>
		//=========================================================================================
		private void InitializeCalendarProc( Object sender, EventArgs myEventArgs )
		{
			switch( this.calenderMode )
			{
			case CalenderMode.FadeIn:
				this.FadeIn();
				break;

			case CalenderMode.Loading:
				this.LoadCalender();
				break;

			case CalenderMode.FadeOut:
				this.FadeOut();
				break;
			}
		}

		//=========================================================================================
		/// <summary>フェードイン</summary>
		//=========================================================================================
		private void FadeIn()
		{
			try
			{
				// 不透明度を1%ずつ上げる
				this.SplashScreen.Opacity += 0.01;

				// 不透明度が100%に到達
				if( 1.0 <= this.SplashScreen.Opacity )
				{
					// 読み込み開始
					this.calenderMode = CalenderMode.Loading;
				}
			}
			catch( Exception e )
			{
				Console.WriteLine( e.Message );

				// 初期化後の処理
				this.AfterInitialize();
			}
		}

		//=========================================================================================
		/// <summary>カレンダー読み込み</summary>
		//=========================================================================================
		private void LoadCalender()
		{
			try
			{
				// カレンダーの日付設定
				this.syachikuAPI.ScheduleMonth = this.newDate;

				// リストクリア
				this.CalendarGrid.Rows.Clear();

				foreach( DateTime date in this.syachikuAPI.ScheduleMonthList )
				{
					DataGridViewRow row = new DataGridViewRow();
					row.CreateCells( this.CalendarGrid );
					
					// 列挙した日付が休日に含まれているか検索
					int offDay = this.syachikuAPI.OffDayList.FindIndex( delegate( DateTime found ) { return found == date; } );
					if( 0 <= offDay )
					{
						// 休日は文字色を赤、チェックボックスを設定不可にする
						row.DefaultCellStyle.ForeColor = Color.Red;
						row.Cells[ GridColumns.OffDay ].ReadOnly = true;
						row.Cells[ GridColumns.OnTime ].ReadOnly = true;
					}
					else
					{
						// 通常日は文字色を黒、チェックボックスを設定可能にする
						row.DefaultCellStyle.ForeColor = Color.Black;
						row.Cells[ GridColumns.OffDay ].ReadOnly = false;
						row.Cells[ GridColumns.OnTime ].ReadOnly = false;
					}

					// 予定有給日有無取得
					int paidHoliday = this.syachikuAPI.PaidHolidayList.FindIndex( delegate( DateTime found ) { return found == date; } );

					// 予定定退日有無取得
					int onTimeDay = this.syachikuAPI.OnTimeDayList.FindIndex( delegate( DateTime found ) { return found == date; } );

					// 有休有無
					row.Cells[ GridColumns.OffDay ].Value = ( 0 <= paidHoliday );
					row.Cells[ GridColumns.OffDay ].Style.BackColor = Color.Silver;
					// 定退有無
					row.Cells[ GridColumns.OnTime ].Value = ( 0 <= onTimeDay );
					row.Cells[ GridColumns.OnTime ].Style.BackColor = Color.Silver;
					// 日付
					row.Cells[ GridColumns.Day ].Value = date.ToString( "MM/dd (ddd)" );
					row.Cells[ GridColumns.Day ].Style.BackColor = Color.Silver;
					// 終業時刻
					row.Cells[ GridColumns.EndTime ].Value = String.Empty;
					row.Cells[ GridColumns.EndTime ].Style.BackColor = Color.Silver;
					// 残業時間
					row.Cells[ GridColumns.OverTime ].Value = String.Empty;
					row.Cells[ GridColumns.OverTime ].ReadOnly = true;

					this.CalendarGrid.Rows.Add( row );
				}

				// 計算結果表示
				this.ShowCalcResult();

				this.calenderMode = CalenderMode.FadeOut;
			}
			catch( Exception e )
			{
				Console.WriteLine( e.Message );

				// 初期化後の処理
				this.AfterInitialize();
			}
		}

		//=========================================================================================
		/// <summary>フェードアウト</summary>
		//=========================================================================================
		private void FadeOut()
		{
			try
			{
				// 不透明度を1%づつ下げる
				this.SplashScreen.Opacity -= 0.01;

				// 不透明度0%に到達
				if( this.SplashScreen.Opacity <= 0.0 )
				{
					// 初期化後の処理
					this.AfterInitialize();

					// 読み込み完了
					this.calenderMode = CalenderMode.LoadComplate;
				}
			}
			catch( Exception e )
			{
				Console.WriteLine( e.Message );

				// 初期化後の処理
				this.AfterInitialize();
			}
		}

		//=========================================================================================
		/// <summary>計算結果表示</summary>
		//=========================================================================================
		private void ShowCalcResult()
		{
			if( this.syachikuAPI.OverWorkDayList == null )
			{
				return;
			}

			this.isAutoSet = true;

			for( int i = 0; i < this.syachikuAPI.ScheduleMonthList.Count; i++ )
			{
				DateTime date = this.syachikuAPI.ScheduleMonthList[ i ];

				// 読み取り専用に設定
				this.CalendarGrid[ GridColumns.OverTime, i ].ReadOnly = true;

				// 列挙した日付が休日に含まれているか検索
				int foundOffDayIndex = syachikuAPI.OffDayList.FindIndex( delegate( DateTime found ) { return found == date; } );
				if( 0 <= foundOffDayIndex )
				{
					// 休日には残業時間を入れない
					this.CalendarGrid[ GridColumns.EndTime, i ].Value = String.Empty;
					this.CalendarGrid[ GridColumns.OverTime, i ].Value = String.Empty;
					continue;
				}

				// 列挙した日付が休日に含まれているか検索
				int paidHolidayIndex = syachikuAPI.PaidHolidayList.FindIndex( delegate( DateTime found ) { return found == date; } );
				if( 0 <= paidHolidayIndex )
				{
					// 休日には残業時間を入れない
					this.CalendarGrid[ GridColumns.EndTime, i ].Value = String.Empty;
					this.CalendarGrid[ GridColumns.OverTime, i ].Value = String.Empty;
					continue;
				}

				String endTimeText = String.Empty;
				String overTimeText = String.Empty;

				// 残業時間が設定された日付がカレンダー上のどこにあるかを検索
				int foundOverTimeIndex = this.syachikuAPI.OverWorkDayList.FindIndex( delegate( 残業日いんたーふぇーす found ) { return found.GetDate() == date; } );
				if( 0 <= foundOverTimeIndex )
				{
					endTimeText = this.syachikuAPI.OverWorkDayList[ foundOverTimeIndex ].GetEndTime();
					overTimeText = this.syachikuAPI.OverWorkDayList[ foundOverTimeIndex ].GetOverWorkTime();

					// 編集許可
					this.CalendarGrid[ GridColumns.OverTime, i ].ReadOnly = false;
				}

				this.CalendarGrid[ GridColumns.EndTime, i ].Value = endTimeText;
				this.CalendarGrid[ GridColumns.OverTime, i ].Value = overTimeText;
			}

			// 稼働日数
			this.WorkDayText.Text = this.syachikuAPI.workDay.ToString();

			// 有給日数
			this.PaidHolidayText.Text = this.syachikuAPI.PaidHolidayList.Count.ToString();

			// 定退日数
			this.OnTimeDayText.Text = this.syachikuAPI.OnTimeDayList.Count.ToString();

			// 累計残業
			this.OverTimeText.Text = this.syachikuAPI.TotalOverWorkTime;

			this.isAutoSet = false;
		}

		//=========================================================================================
		/// <summary>新規計算</summary>
		//=========================================================================================
		private void NewCalc()
		{

			try
			{
				// ノルマ時間
				this.syachikuAPI.QuataTime = ( double )this.QuataUpDown.Value;

				// 超過時間
				this.syachikuAPI.CurrentOverTime = ( double )this.OverTimeUpDown.Value;

				// 予定有給日のリスト
				List<DateTime> paidHolidayList = new List<DateTime>();

				// 定時退社日のリスト
				List<DateTime> onTimeDayList = new List<DateTime>();

				foreach( DataGridViewRow row in this.CalendarGrid.Rows )
				{
					// 予定有給日にチェックがついている場合
					if( ( bool )row.Cells[ GridColumns.OffDay ].Value == true )
					{
						DateTime paidHoliday = DateTime.Parse( ( String )row.Cells[ GridColumns.Day ].Value );
						paidHolidayList.Add( paidHoliday );
					}

					// 予定定時退社日にチェックがついている場合
					if( ( bool )row.Cells[ GridColumns.OnTime ].Value == true )
					{
						DateTime onTime = DateTime.Parse( ( String )row.Cells[ GridColumns.Day ].Value );
						onTimeDayList.Add( onTime );
					}
				}

				// 予定有給日セット
				this.syachikuAPI.PaidHolidayList = paidHolidayList;

				// 予定定退日セット
				this.syachikuAPI.OnTimeDayList = onTimeDayList;

				// 残業計算実行
				this.syachikuAPI.OverTimeCalculation();
			}
			catch( Exception e )
			{
				MessageBox.Show( e.Message, "計算できません", MessageBoxButtons.OK, MessageBoxIcon.Error );
			}
			finally
			{
				ShowCalcResult();
			}
		}

		//=========================================================================================
		/// <summary>補正計算</summary>
		//=========================================================================================
		private void AdjustCalc()
		{
			try
			{
				// ノルマ時間
				this.syachikuAPI.QuataTime = ( double )this.QuataUpDown.Value;

				// 超過時間
				this.syachikuAPI.CurrentOverTime = ( double )this.OverTimeUpDown.Value;

				// 予定有給日のリスト
				List<DateTime> paidHolidayList = new List<DateTime>();

				// 定時退社日のリスト
				List<DateTime> onTimeDayList = new List<DateTime>();

				foreach( DataGridViewRow row in this.CalendarGrid.Rows )
				{
					// 予定有給日にチェックがついている場合
					if( ( bool )row.Cells[ GridColumns.OffDay ].Value == true )
					{
						DateTime paidHoliday = DateTime.Parse( Convert.ToString( row.Cells[ GridColumns.Day ].Value ) );
						paidHolidayList.Add( paidHoliday );
					}

					// 予定定時退社日にチェックがついている場合
					if( ( bool )row.Cells[ GridColumns.OnTime ].Value == true )
					{
						DateTime onTime = DateTime.Parse( Convert.ToString( row.Cells[ GridColumns.Day ].Value ) );
						onTimeDayList.Add( onTime );
					}
				}

				for( int i = 0; i < this.syachikuAPI.ScheduleMonthList.Count; i++ )
				{
					if( String.IsNullOrEmpty( Convert.ToString( this.CalendarGrid[ GridColumns.OverTime, i ].Value ) ) == false )
					{
						DateTime date = this.syachikuAPI.ScheduleMonthList[ i ];
						// 残業時間が設定された日付がカレンダー上のどこにあるかを検索
						int foundOverTimeIndex = this.syachikuAPI.OverWorkDayList.FindIndex( delegate( 残業日いんたーふぇーす found ) { return found.GetDate() == date; } );
						if( 0 <= foundOverTimeIndex )
						{
							this.syachikuAPI.OverWorkDayList[ foundOverTimeIndex ].adjustOverWorkTime = Convert.ToDouble( this.CalendarGrid[ GridColumns.OverTime, i ].Value );
						}
					}
				}

				// 予定有給日セット
				this.syachikuAPI.PaidHolidayList = paidHolidayList;

				// 予定定退日セット
				this.syachikuAPI.OnTimeDayList = onTimeDayList;

				// 残業計算実行
				this.syachikuAPI.OverTimeAdjust();
			}
			catch( Exception e )
			{
				MessageBox.Show( e.Message, "計算できません", MessageBoxButtons.OK, MessageBoxIcon.Error );
			}
			finally
			{
				ShowCalcResult();
			}
		}
		#endregion

		#region フィールド
		/// <summary>社畜API</summary>
		private 社畜API syachikuAPI = new 社畜API();

		/// <summary>初期化する日付</summary>
		private DateTime newDate = new DateTime();

		/// <summary>社畜API</summary>
		private SplashScreenWindow SplashScreen = new SplashScreenWindow();

		/// <summary>スプラッシュスクリーンタイマー</summary>
		Timer splashScreenTimer = null;

		/// <summary>カレンダー読み込みモード</summary>
		private CalenderMode calenderMode = CalenderMode.LoadStart;
				
		// 計算時セットフラグ
		private bool isAutoSet = false;
		#endregion

		#region 定数
		/// <summary>カラム種別</summary>
		private class GridColumns
		{
			internal const int OffDay = 0;
			internal const int OnTime = 1;
			internal const int Day = 2;
			internal const int EndTime = 3;
			internal const int OverTime = 4;
		}

		enum CalenderMode
		{
			LoadStart = 0,
			FadeIn = 1,
			Loading = 2,
			FadeOut = 3,
			LoadComplate = 4,
		}
		#endregion
	}
}
